﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace Borg.AddOn
{
    [Serializable]
    public sealed class HookRegistry
    {
        private readonly List<IActivatorSubstitutionHook> _activatorSubstitutionHooks;
        private readonly List<IComposerApiHook> _composerApiHooks;
        private readonly List<IComposerNotificationHook> _composerNotificationHooks;
        private readonly List<IComposerSubstitutionHook> _composerSubstitutionHooks;
        private readonly List<ICompositionEngineApiHook> _compositionEngineApiHooks;
        private readonly List<ICompositionEngineNotificationHook> _compositionEngineNotificationHooks;
        private readonly List<ICompositionPermissionHook> _compositionPermissionHooks;
        private readonly List<IGarbageCollectorApiHook> _garbageCollectorApiHooks;
        private readonly HashSet<Assembly> _hookAssemblies;
        private readonly List<IInstanceStoreSubstitutionHook> _instanceStoreSubstitutionHooks;
        private readonly List<IMethodInvokerNotificationHook> _methodInvokerNotificationHooks;
        private readonly List<INotifierNotificationHook> _notifierNotificationHooks;
        private readonly Runtime _runtime;
        private readonly List<ITypeStoreModifierApiHook> _typeStoreModifierApiHooks;
        private readonly List<ITypeStoreNotificationHook> _typeStoreNotificationHooks;
        private readonly List<ITypeStorePermissionHook> _typeStorePermissionHooks;

        internal Runtime Runtime
        {
            get { return _runtime; }
        }

        internal HookRegistry(Runtime runtime)
        {
            _runtime = runtime;
            _hookAssemblies = new HashSet<Assembly>();
            _activatorSubstitutionHooks = new List<IActivatorSubstitutionHook>();
            _composerApiHooks = new List<IComposerApiHook>();
            _composerNotificationHooks = new List<IComposerNotificationHook>();
            _composerSubstitutionHooks = new List<IComposerSubstitutionHook>();
            _compositionEngineApiHooks = new List<ICompositionEngineApiHook>();
            _compositionEngineNotificationHooks = new List<ICompositionEngineNotificationHook>();
            _garbageCollectorApiHooks = new List<IGarbageCollectorApiHook>();
            _instanceStoreSubstitutionHooks = new List<IInstanceStoreSubstitutionHook>();
            _methodInvokerNotificationHooks = new List<IMethodInvokerNotificationHook>();
            _notifierNotificationHooks = new List<INotifierNotificationHook>();
            _typeStoreNotificationHooks = new List<ITypeStoreNotificationHook>();
            _typeStorePermissionHooks = new List<ITypeStorePermissionHook>();
            _typeStoreModifierApiHooks = new List<ITypeStoreModifierApiHook>();
            _compositionPermissionHooks = new List<ICompositionPermissionHook>();
        }

        public void Add(IActivatorSubstitutionHook hook)
        {
            Add(_activatorSubstitutionHooks, hook);
        }

        public void Add(IComposerApiHook hook)
        {
            Add(_composerApiHooks, hook);
        }

        public void Add(IComposerNotificationHook hook)
        {
            Add(_composerNotificationHooks, hook);
        }

        public void Add(IComposerSubstitutionHook hook)
        {
            Add(_composerSubstitutionHooks, hook);
        }

        public void Add(ICompositionEngineApiHook hook)
        {
            Add(_compositionEngineApiHooks, hook);
        }

        public void Add(ICompositionEngineNotificationHook hook)
        {
            Add(_compositionEngineNotificationHooks, hook);
        }

        public void Add(ICompositionPermissionHook hook)
        {
            Add(_compositionPermissionHooks, hook);
        }

        public void Add(IGarbageCollectorApiHook hook)
        {
            Add(_garbageCollectorApiHooks, hook);
        }

        public void Add(IInstanceStoreSubstitutionHook hook)
        {
            Add(_instanceStoreSubstitutionHooks, hook);
        }

        public void Add(IMethodInvokerNotificationHook hook)
        {
            Add(_methodInvokerNotificationHooks, hook);
        }

        public void Add(INotifierNotificationHook hook)
        {
            Add(_notifierNotificationHooks, hook);
        }

        public void Add(ITypeStoreModifierApiHook hook)
        {
            Add(_typeStoreModifierApiHooks, hook);
        }

        public void Add(ITypeStoreNotificationHook hook)
        {
            Add(_typeStoreNotificationHooks, hook);
        }

        public void Add(ITypeStorePermissionHook hook)
        {
            Add(_typeStorePermissionHooks, hook);
        }

        private void Add<T>(List<T> list, T hook) where T: class
        {
            if (hook == null)
                throw new ArgumentException("hook");

            _hookAssemblies.Add(hook.GetType().Assembly);
            list.Add(hook);
        }

        internal IActivatorSubstitutionHook CreateActivatorSubstitutionHook()
        {
            return CreateHook(_activatorSubstitutionHooks, () =>
                new ActivatorSubstitutionHook(), hooks =>
                    new ActivatorSubstitutionHookChain(hooks));
        }

        internal IComposerApiHook CreateComposerApiHook()
        {
            return CreateHook(_composerApiHooks, () =>
                new ComposerApiHook(), hooks =>
                    new ComposerApiHookChain(hooks));
        }

        internal IComposerNotificationHook CreateComposerNotificationHook()
        {
            return CreateHook(_composerNotificationHooks, () =>
                new ComposerNotificationHook(), hooks =>
                    new ComposerNotificationHookChain(hooks));
        }

        internal IComposerSubstitutionHook CreateComposerSubstitutionHook()
        {
            return CreateHook(_composerSubstitutionHooks, () =>
                new ComposerSubstitutionHook(), hooks =>
                    new ComposerSubstitutionHookChain(hooks));
        }

        internal ICompositionEngineApiHook CreateCompositionEngineApiHook()
        {
            return CreateHook(_compositionEngineApiHooks, () =>
                new CompositionEngineApiHook(), hooks =>
                    new CompositionEngineApiHookChain(hooks));
        }

        internal ICompositionEngineNotificationHook CreateCompositionEngineNotificationHook()
        {
            return CreateHook(_compositionEngineNotificationHooks, () =>
                new CompositionEngineNotificationHook(), hooks =>
                    new CompositionEngineNotificationHookChain(hooks));
        }

        internal ICompositionPermissionHook CreateCompositionPermissionHook()
        {
            return CreateHook(_compositionPermissionHooks, () =>
                new CompositionPermissionHook(), hooks =>
                    new CompositionPermissionHookChain(hooks));
        }

        internal IGarbageCollectorApiHook CreateGarbageCollectorApiHook()
        {
            return CreateHook(_garbageCollectorApiHooks, () =>
                new GarbageCollectorApiHook(), hooks =>
                    new GarbageCollectorApiHookChain(hooks));
        }

        private static T CreateHook<T>(List<T> hooks, Func<T> createEmpty, Func<T[], T> create)
        {
            switch (hooks.Count)
            {
                case 0:
                    return createEmpty();

                
                case 1:
                    return hooks[0];
            }

            return create(hooks.ToArray());
        }

        internal IInstanceStoreSubstitutionHook CreateInstanceStoreSubstitutionHook()
        {
            return CreateHook(_instanceStoreSubstitutionHooks, () =>
                new InstanceStoreSubstitutionHook(), hooks =>
                    new InstanceStoreSubstitutionHookChain(hooks));
        }

        internal IMethodInvokerNotificationHook CreateMethodInvokerNotificationHook()
        {
            return CreateHook(_methodInvokerNotificationHooks, () =>
                new MethodInvokerNotificationHook(), hooks =>
                    new MethodInvokerNotificationHookChain(hooks));
        }

        internal INotifierNotificationHook CreateNotifierNotificationHook()
        {
            return CreateHook(_notifierNotificationHooks, () =>
                new NotifierNotificationHook(), hooks =>
                    new NotifierNotificationHookChain(hooks));
        }

        internal ITypeStoreModifierApiHook CreateTypeStoreModifierApiHook()
        {
            return CreateHook(_typeStoreModifierApiHooks, () =>
                new TypeStoreModifierApiHook(), hooks =>
                    new TypeStoreModifierApiHookChain(hooks));
        }

        internal ITypeStoreNotificationHook CreateTypeStoreNotificationHook()
        {
            return CreateHook(_typeStoreNotificationHooks, () =>
                new TypeStoreNotificationHook(), hooks =>
                    new TypeStoreNotificationHookChain(hooks));
        }

        internal ITypeStorePermissionHook CreateTypeStorePermissionHook()
        {
            return CreateHook(_typeStorePermissionHooks, () =>
                new TypeStorePermissionHook(), hooks =>
                    new TypeStorePermissionHookChain(hooks));
        }

        internal List<Assembly> GetHookAssemblies()
        {
            return new List<Assembly>(_hookAssemblies);
        }
    }
}
